package com.wodan.zhongjinjicang.common.orm.metadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;

import com.wodan.zhongjinjicang.common.orm.annotation.AutoGenernateSql;
import com.wodan.zhongjinjicang.common.orm.annotation.QueryConditionField;
import com.wodan.zhongjinjicang.common.orm.annotation.UpdateSetField;
import com.wodan.zhongjinjicang.common.orm.utils.ReflectionUtils;
import com.wodan.zhongjinjicang.common.orm.utils.ReflectionUtils.MethodParameterAnnotationCallback;
import org.apache.ibatis.mapping.SqlCommandType;
import org.springframework.util.Assert;

import com.google.common.collect.Maps;

/**
 * 方法的元数据
 * 
 * @ClassName: MethodMetaData
 * @author Administrator
 * @date 2015-11-6 下午4:17:41
 * @history
 * 
 */
public class MethodMetaData {

	private final Method method;

	private final SqlCommandType sqlCommandType;

	private final Map<String, Integer> updateSetFieldArgIndexMap;

	private final Map<String, Integer> queryConditionFieldArgIndexMap;

	public MethodMetaData(Method method) {
		Assert.notNull(method);

		this.method = method;
		this.sqlCommandType = resolveSqlCommandType();
		this.updateSetFieldArgIndexMap = resolveUpdateSetFieldArgIndexMap();
		this.queryConditionFieldArgIndexMap = resolveQueryConditionFieldArgIndexMap();
	}

	private Map<String, Integer> resolveQueryConditionFieldArgIndexMap() {
		final Map<String, Integer> resultMap = Maps.newHashMap();

		ReflectionUtils.doWithMethodParameterAnnotations(method, new MethodParameterAnnotationCallback() {
			@Override
			public void doWith(Annotation[] annotations, int paramIndex) {
				for (Annotation annotation : annotations) {
					if (annotation.annotationType().equals(QueryConditionField.class)) {
						QueryConditionField queryConditionField = (QueryConditionField) annotation;
						String fieldName = queryConditionField.fieldName();
						if (resultMap.containsKey(fieldName)) {
							throw new IllegalArgumentException("方法[" + method
									+ "]参数中的@QueryConditionField属性定义了相同的fieldName[" + fieldName + "]");
						}
						resultMap.put(fieldName, Integer.valueOf(paramIndex));
					}
				}
			}
		});

		return resultMap;
	}

	private Map<String, Integer> resolveUpdateSetFieldArgIndexMap() {
		final Map<String, Integer> resultMap = Maps.newHashMap();

		ReflectionUtils.doWithMethodParameterAnnotations(method, new MethodParameterAnnotationCallback() {
			@Override
			public void doWith(Annotation[] annotations, int paramIndex) {
				for (Annotation annotation : annotations) {
					if (annotation.annotationType().equals(UpdateSetField.class)) {
						UpdateSetField updateSetField = (UpdateSetField) annotation;
						String fieldName = updateSetField.fieldName();
						if (resultMap.containsKey(fieldName)) {
							throw new IllegalArgumentException("方法[" + method
									+ "]参数中的@UpdateSetField属性定义了相同的fieldName[" + fieldName + "]");
						}
						resultMap.put(fieldName, Integer.valueOf(paramIndex));
					}
				}
			}
		});

		return resultMap;
	}

	private SqlCommandType resolveSqlCommandType() {
		AutoGenernateSql annotation = method.getAnnotation(AutoGenernateSql.class);
		if (annotation == null) {
			throw new IllegalArgumentException("method [" + method + "] 必须标记为@AutoGenernateSql");
		}

		return annotation.sqlType();
	}

	public SqlCommandType getSqlCommandType() {
		return sqlCommandType;
	}
	
	public Method getMethod() {
		return method;
	}

	public Map<String, Object> resolveSetFieldNameValueMap(Object[] args) {
		return resolveFromFieldNameIndexMap(args, updateSetFieldArgIndexMap);
	}

	private Map<String, Object> resolveFromFieldNameIndexMap(Object[] args, Map<String, Integer> fieldNameIndexMap) {
		Map<String, Object> resultMap = Maps.newHashMapWithExpectedSize(fieldNameIndexMap.size());

		for (Map.Entry<String, Integer> entry : fieldNameIndexMap.entrySet()) {
			String fieldName = entry.getKey();
			Integer paramIndex = entry.getValue();

			resultMap.put(fieldName, args[paramIndex.intValue()]);
		}

		return resultMap;
	}

	public Map<String, Object> resolveQueryConditionFieldNameValueMap(Object[] args) {
		return resolveFromFieldNameIndexMap(args, queryConditionFieldArgIndexMap);
	}
}
